
#ifndef ACEL_INET_ADDR_HPP
#define ACEL_INET_ADDR_HPP

#include    <sys/types.h>
#include    <string.h>
#include    <sys/socket.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <netdb.h>

#include    "acel/addr.hpp"

namespace   acel
{

// intel chip is Little-Endian byte order
#define ACE_LITTLE_ENDIAN


    
#ifndef ACE_MAX_DEFAULT_PORT
#define ACE_MAX_DEFAULT_PORT    65535
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  256
#endif

#ifndef ENOTSUP
#define ENOTSUP ENOSYS
#endif
#define ACE_NOTSUP_RETURN(FAILVALUE) do { errno = ENOTSUP; fprintf (stderr, "ACE_NOTSUP: %s, line %d\n", __FILE__, __LINE__); return FAILVALUE; } while (0)

// Byte swapping macros to deal with differences between little endian
// and big endian machines.  Note that "long" here refers to 32 bit
// quantities.
# define ACE_SWAP_LONG(L) ((ACE_SWAP_WORD ((L) & 0xFFFF) << 16) \
            | ACE_SWAP_WORD(((L) >> 16) & 0xFFFF))
# define ACE_SWAP_WORD(L) ((((L) & 0x00FF) << 8) | (((L) & 0xFF00) >> 8))

# if defined (ACE_LITTLE_ENDIAN)
#   define ACE_HTONL(X) ACE_SWAP_LONG (X)
#   define ACE_NTOHL(X) ACE_SWAP_LONG (X)
#   define ACE_IDL_NCTOHL(X) (X)
#   define ACE_IDL_NSTOHL(X) (X)
# else
#   define ACE_HTONL(X) X
#   define ACE_NTOHL(X) X
#   define ACE_IDL_NCTOHL(X) (X << 24)
#   define ACE_IDL_NSTOHL(X) ((X) << 16)
# endif /* ACE_LITTLE_ENDIAN */

# if defined (ACE_LITTLE_ENDIAN)
#   define ACE_HTONS(x) ACE_SWAP_WORD(x)
#   define ACE_NTOHS(x) ACE_SWAP_WORD(x)
# else
#   define ACE_HTONS(x) x
#   define ACE_NTOHS(x) x
# endif /* ACE_LITTLE_ENDIAN */

/**
 * @class INET_Addr
 *
 * @brief Defines a C++ wrapper facade for the Internet domain address family format.
 */
class INET_Addr : public Addr
{
public:
    // = Initialization methods.

    /// Default constructor.
    INET_Addr (void);

    /// Copy constructor.
    INET_Addr (const INET_Addr &);

    /// Creates an INET_Addr from a sockaddr_in structure.
    INET_Addr (const sockaddr_in *addr, int len);

    /// Creates an INET_Addr from a @a port_number and the remote
    /// @a host_name. The port number is assumed to be in host byte order.
    /// To set a port already in network byte order, please @see set().
    /// Use address_family to select IPv6 (PF_INET6) vs. IPv4 (PF_INET).
    INET_Addr (u_short port_number,
                 const char host_name[],
                 int address_family = AF_UNSPEC);

    /**
     * Initializes an INET_Addr from the @a address, which can be
     * "ip-number:port-number" (e.g., "tango.cs.wustl.edu:1234" or
     * "128.252.166.57:1234").  If there is no ':' in the @a address it
     * is assumed to be a port number, with the IP address being
     * INADDR_ANY.
     */
    explicit INET_Addr (const char address[],
                          int address_family = AF_UNSPEC);

    /**
     * Creates an INET_Addr from a @a port_number and an Internet
     * <ip_addr>.  This method assumes that @a port_number and <ip_addr>
     * are in host byte order. If you have addressing information in
     * network byte order, @see set().
     */
    explicit INET_Addr (u_short port_number,
                          uint32_t ip_addr = INADDR_ANY);

    /// Uses <getservbyname> to create an ACE_INET_Addr from a
    /// <port_name>, the remote @a host_name, and the @a protocol.
    INET_Addr (const char port_name[],
                 const char host_name[],
                 const char protocol[] = "tcp");

    /**
     * Uses <getservbyname> to create an ACE_INET_Addr from a
     * <port_name>, an Internet <ip_addr>, and the @a protocol.  This
     * method assumes that <ip_addr> is in host byte order.
     */
    INET_Addr (const char port_name[],
                 uint32_t ip_addr,
                 const char protocol[] = "tcp");

    /// Default dtor.
    ~INET_Addr (void);

    // = Direct initialization methods.

    // These methods are useful after the object has been constructed.

    /// Initializes from another ACE_INET_Addr.
    int set (const INET_Addr &);

    /**
     * Initializes an INET_Addr from a @a port_number and the
     * remote @a host_name.  If @a encode is non-zero then @a port_number is
     * converted into network byte order, otherwise it is assumed to be
     * in network byte order already and are passed straight through.
     * address_family can be used to select IPv4/IPv6 if the OS has
     * IPv6 capability (ACE_HAS_IPV6 is defined). To specify IPv6, use
     * the value AF_INET6. To specify IPv4, use AF_INET.
     */
    int set (u_short port_number,
           const char host_name[],
           int encode = 1,
           int address_family = AF_UNSPEC);

    /**
     * Initializes an INET_Addr from a @a port_number and an Internet
     * @a ip_addr.  If @a encode is non-zero then the port number and IP address
     * are converted into network byte order, otherwise they are assumed to be
     * in network byte order already and are passed straight through.
     *
     * If <map> is non-zero and IPv6 support has been compiled in,
     * then this address will be set to the IPv4-mapped IPv6 address of it.
     */
    int set (u_short port_number,
           uint32_t ip_addr = INADDR_ANY,
           int encode = 1,
           int map = 0);

    /// Uses <getservbyname> to initialize an INET_Addr from a
    /// <port_name>, the remote @a host_name, and the @a protocol.
    int set (const char port_name[],
           const char host_name[],
           const char protocol[] = "tcp");

    /**
     * Uses <getservbyname> to initialize an INET_Addr from a
     * <port_name>, an <ip_addr>, and the @a protocol.  This assumes that
     * <ip_addr> is already in network byte order.
     */
    int set (const char port_name[],
           uint32_t ip_addr,
           const char protocol[] = "tcp");

    /**
     * Initializes an INET_Addr from the @a addr, which can be
     * "ip-number:port-number" (e.g., "tango.cs.wustl.edu:1234" or
     * "128.252.166.57:1234").  If there is no ':' in the @a address it
     * is assumed to be a port number, with the IP address being
     * INADDR_ANY.
     */
    int set (const char addr[], int address_family = AF_UNSPEC);

    /// Creates an INET_Addr from a sockaddr_in structure.
    int set (const sockaddr_in *,
           int len);

    /// Return a pointer to the underlying network address.
    virtual void *getAddr (void) const;
    int getAddrSize(void) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType () == PF_INET)
        return sizeof this->_inet_addr.in4_;
      else
        return sizeof this->_inet_addr.in6_;
#else
      return sizeof this->_inet_addr.in4_;
#endif /* ACE_HAS_IPV6 */
    }

    /// Set a pointer to the address.
    virtual void setAddr (void *, int len);

    /// Set a pointer to the address.
    virtual void setAddr (void *, int len, int map);

    /**
     * Transform the current INET_Addr address into string format.
     * If <ipaddr_format> is non-0 this produces "ip-number:port-number"
     * (e.g., "128.252.166.57:1234"), whereas if <ipaddr_format> is 0
     * this produces "ip-name:port-number" (e.g.,
     * "tango.cs.wustl.edu:1234").  Returns -1 if the @a size of the
     * <buffer> is too small, else 0.
     */
    virtual int toString (char buffer[],
                              size_t size,
                              int ipaddr_format = 1) const;

    /**
     * Initializes an INET_Addr from the @a address, which can be
     * "ip-addr:port-number" (e.g., "tango.cs.wustl.edu:1234"),
     * "ip-addr:port-name" (e.g., "tango.cs.wustl.edu:telnet"),
     * "ip-number:port-number" (e.g., "128.252.166.57:1234"), or
     * "ip-number:port-name" (e.g., "128.252.166.57:telnet").  If there
     * is no ':' in the @a address it is assumed to be a port number,
     * with the IP address being INADDR_ANY.
     */
    virtual int fromString (const char address[],
                              int address_family = AF_UNSPEC);

    /**
     * Sets the port number without affecting the host name.  If
     * @a encode is enabled then @a port_number is converted into network
     * byte order, otherwise it is assumed to be in network byte order
     * already and are passed straight through.
     */
    void setPortNumber (u_short,
                        int encode = 1);

    /**
     * Sets the address without affecting the port number.  If
     * @a encode is enabled then <ip_addr> is converted into network
     * byte order, otherwise it is assumed to be in network byte order
     * already and are passed straight through.  The size of the address
     * is specified in the @a len parameter.
     * If <map> is non-zero, IPv6 support has been compiled in, and
     * <ip_addr> is an IPv4 address, then this address is set to the IPv4-mapped
     * IPv6 address of it.
     */
    int setAddress (const char *ip_addr,
                   int len,
                   int encode = 1,
                   int map = 0);

    #if (defined (__linux__) || defined (ACE_WIN32)) && defined (ACE_HAS_IPV6)
    /**
     * Sets the interface that should be used for this address. This only has
     * an effect when the address is link local, otherwise it does nothing.
     */
    int setInterface (const char *intf_name);
    #endif /* (__linux__ || ACE_WIN32) && ACE_HAS_IPV6 */

    /// Return the port number, converting it into host byte-order.
    u_short getPortNumber (void) const
    {
#if defined (ACE_LACKS_NTOHS)
      ACE_NOTSUP_RETURN (0);
#elif defined (ACE_HAS_IPV6)
      if (this->getType () == PF_INET)
        return ntohs (this->_inet_addr.in4_.sin_port);
      else
        return ntohs (this->_inet_addr.in6_.sin6_port);
#else
      return ntohs (this->_inet_addr.in4_.sin_port);
#endif /* ACE_HAS_IPV6 */
    }

    /**
     * Return the character representation of the name of the host,
     * storing it in the <hostname> (which is assumed to be
     * <hostnamelen> bytes long).  This version is reentrant.  If
     * <hostnamelen> is greater than 0 then <hostname> will be
     * NUL-terminated even if -1 is returned.
     */
    int getHostName (char hostname[],
                     size_t hostnamelen) const;

    /**
     * Return the character representation of the hostname.  This
     * version is non-reentrant since it returns a pointer to a static
     * data area.  You should therefore either (1) do a "deep copy" of
     * the address returned by get_host_name(), e.g., using strdup() or
     * (2) use the "reentrant" version of get_host_name() described
     * above.
     */
    const char *getHostName (void) const;

    /**
     * Return the "dotted decimal" Internet address representation of
     * the hostname storing it in the @a addr (which is assumed to be
     * @a addr_size bytes long).  This version is reentrant.
     */
    const char *getHostAddr (char *addr, int addr_size) const;

    /**
     * Return the "dotted decimal" Internet address representation of
     * the hostname.  This version is non-reentrant since it returns a
     * pointer to a static data area.  You should therefore either
     * (1) do a "deep copy" of the address returned by get_host_addr(), e.g.,
     * using strdup() or (2) use the "reentrant" version of
     * get_host_addr() described above.
     */
    const char *getHostAddr (void) const;

    /// Return the 4-byte IP address, converting it into host byte
    /// order.
    uint32_t getIPAddress (void) const;

    /// Return @c true if the IP address is INADDR_ANY or IN6ADDR_ANY.
    bool isAny (void) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType () == AF_INET6)
          return IN6_IS_ADDR_UNSPECIFIED (&this->_inet_addr.in6_.sin6_addr);
#endif /* ACE_HAS_IPV6 */

      return (this->_inet_addr.in4_.sin_addr.s_addr == INADDR_ANY);
    }

    /// Return @c true if the IP address is IPv4/IPv6 loopback address.
    bool isLoopback (void) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType () == AF_INET6)
          return IN6_IS_ADDR_LOOPBACK (&this->_inet_addr.in6_.sin6_addr);
#endif /* ACE_HAS_IPV6 */

      // RFC 3330 defines loopback as any address with 127.x.x.x
      return ((this->getIPAddress () & 0XFF000000) == (INADDR_LOOPBACK & 0XFF000000));
    }

    /// Return @c true if the IP address is IPv4/IPv6 multicast address.
    bool isMulticast (void) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType() == AF_INET6)
        return this->_inet_addr.in6_.sin6_addr.s6_addr[0] == 0xFF;
#endif /* ACE_HAS_IPV6 */
      return
        this->_inet_addr.in4_.sin_addr.s_addr >= 0xE0000000 &&  // 224.0.0.0
        this->_inet_addr.in4_.sin_addr.s_addr <= 0xEFFFFFFF; // 239.255.255.255
    }

#if defined (ACEL_HAS_IPV6)
    /// Return @c true if the IP address is IPv6 linklocal address.
    bool is_linklocal (void) const
    {
      if (this->getType () == AF_INET6)
          return IN6_IS_ADDR_LINKLOCAL (&this->_inet_addr.in6_.sin6_addr);

      return false;
    }

    /// Return @c true if the IP address is IPv4-mapped IPv6 address.
    bool isIPv4MappedIPv6 (void) const
    {
      if (this->getType () == AF_INET6)
          return IN6_IS_ADDR_V4MAPPED (&this->_inet_addr.in6_.sin6_addr);

      return false;
    }

    /// Return @c true if the IP address is IPv4-compatible IPv6 address.
    bool isIPv4CompatIPv6 (void) const
    {
      if (this->getType () == AF_INET6)
          return IN6_IS_ADDR_V4COMPAT (&this->_inet_addr.in6_.sin6_addr);

      return false;
    }
#endif /* ACEL_HAS_IPV6 */

    /**
     * Returns @c true if @c this is less than @a rhs.  In this context,
     * "less than" is defined in terms of IP address and TCP port
     * number.  This operator makes it possible to use @c ACE_INET_Addrs
     * in STL maps.
     */
    bool operator < (const INET_Addr &rhs) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType() != rhs.getType())
      {
        return this->getType() < rhs.getType();
      }

      if (this->getType() == PF_INET6)
      {
        int memval = ACE_OS::memcmp (this->ip_addr_pointer(),
                                     rhs.ip_addr_pointer(),
                                     this->ip_addr_size());

        return memval < 0
                || (memval == 0
                    && (this->get_port_number() < rhs.get_port_number()
                        || (this->get_port_number() == rhs.get_port_number()
                            && this->_inet_addr.in6_.sin6_scope_id <
                                rhs._inet_addr.in6_.sin6_scope_id)));
      }
#endif

      return this->getIPAddress () < rhs.getIPAddress ()
        || (this->getIPAddress () == rhs.getIPAddress ()
            && this->getPortNumber () < rhs.getPortNumber ());
    }
    

    /// Compare two addresses for equality.  The addresses are considered
    /// equal if they contain the same IP address and port number.
    bool operator == (const INET_Addr &SAP) const;

    /// Compare two addresses for inequality.
    bool operator != (const INET_Addr &SAP) const;

    /// A variation of the equality operator, this method only compares the
    /// IP address and ignores the port number.
    bool isIPEqual (const INET_Addr &SAP) const;

    /// Computes and returns hash value.
    virtual unsigned long hash (void) const;

    private:
    /// Insure that @a hostname is properly null-terminated.
    int getHostName_i (char hostname[], size_t hostnamelen) const;

    // Methods to gain access to the actual address of
    // the underlying internet address structure.
    void *IPAddrPointer (void) const
    {
#if defined (ACE_HAS_IPV6)
      if (this->getType () == PF_INET)
        return (void*)&this->_inet_addr.in4_.sin_addr;
      else
        return (void*)&this->_inet_addr.in6_.sin6_addr;
#else
      return (void*)&this->_inet_addr.in4_.sin_addr;
#endif
    }
    
    int IPAddrSize (void) const
    {
      // Since this size value is used to pass to other host db-type
      // functions (gethostbyaddr, etc.) the length is of int type.
      // Thus, cast all these sizes back to int. They're all well
      // within the range of an int anyway.
#if defined (ACE_HAS_IPV6)
      if (this->getType () == PF_INET)
        return static_cast<int> (sizeof this->_inet_addr.in4_.sin_addr);
      else
        return static_cast<int> (sizeof this->_inet_addr.in6_.sin6_addr);
#else
      // These _UNICOS changes were picked up from pre-IPv6 code in
      // get_host_name_i... the IPv6 section above may need something
      // similar, so keep an eye out for it.
#  if !defined(_UNICOS)
      return static_cast<int> (sizeof this->_inet_addr.in4_.sin_addr.s_addr);
#  else /* _UNICOS */
      return static_cast<int> (sizeof this->_inet_addr.in4_.sin_addr);
#  endif /* ! _UNICOS */
#endif /* ACE_HAS_IPV6 */
    }
    
    int determineType (void) const
    {
#if defined (ACE_HAS_IPV6)
#  if defined (ACE_USES_IPV4_IPV6_MIGRATION)
      return ACE::ipv6_enabled () ? AF_INET6 : AF_INET;
#  else
      return AF_INET6;
#  endif /* ACE_USES_IPV4_IPV6_MIGRATION */
#else
      return AF_INET;
#endif /* ACE_HAS_IPV6 */
    }

    /// Initialize underlying _inet_addr to default values
    void reset (void)
    {
      memset (&this->_inet_addr, 0, sizeof (this->_inet_addr));
      if (this->getType() == AF_INET)
        {
#ifdef ACE_HAS_SOCKADDR_IN_SIN_LEN
          this->_inet_addr.in4_.sin_len = sizeof (this->_inet_addr.in4_);
#endif
          this->_inet_addr.in4_.sin_family = AF_INET;
        }
#if defined (ACE_HAS_IPV6)
      else if (this->getType() == AF_INET6)
        {
#ifdef ACE_HAS_SOCKADDR_IN6_SIN6_LEN
          this->_inet_addr.in6_.sin6_len = sizeof (this->_inet_addr.in6_);
#endif
          this->_inet_addr.in6_.sin6_family = AF_INET6;
        }
#endif  /* ACE_HAS_IPV6 */
    }

private :
    /// Underlying representation.
    /// This union uses the knowledge that the two structures share the
    /// first member, sa_family (as all sockaddr structures do).
    union
    {
        sockaddr_in  in4_;
#if defined (ACEL_HAS_IPV6)
        sockaddr_in6 in6_;
#endif /* ACEL_HAS_IPV6 */
    } _inet_addr;

};  // class INET_Addr

}   // namespace acel

#endif  // ACEL_INET_ADDR_HPP
